/*
 * dmuploader.js - Jquery File Uploader - 0.1
 * http://www.daniel.com.uy/projects/jquery-file-uploader/
 *
 * Copyright (c) 2013 Daniel Morales
 * Dual licensed under the MIT and GPL licenses.
 * http://www.daniel.com.uy/doc/license/
 */
(function($) {
  var pluginName = 'dmUploader';
  // These are the plugin defaults values
  var defaults = {
    url : document.URL,
    method : 'POST',
    extraData : {},
    maxFileSize : 0,
    allowedTypes : '*',
    extFilter : null,
    dataType : null,
    fileName : 'files',
    classFiles : 'files',
    classDebug : 'debug-zone',
    templatePreview : '<div class="file-preview"><div class="image-wrapper"><img src="ico/favicon.png" class="image-preview"></div><span class="file-id">#@id@</span> - @filename@ <span class="file-size">(@filesize@)</span><i class="icon-remove" title="REMOVE"></i><br>Status: <span class="file-status">waiting upload</span><div class="progress"><div class="progress-bar" role="progressbar" style="width: 100%;"><span class="sr-only">0% Complete</span></div></div></div>',
    onInit : function() {},
    onFallbackMode : function() {},
    onNewFile : function(id, file) {},
    onBeforeUpload : function(id) {},
    onComplete : function() {},
    onUploadProgress : function(id, percent) {},
    onUploadSuccess : function(id, data) {},
    onUploadError : function(id, message) {},
    onFileTypeError : function(file) {},
    onFileSizeError : function(file) {},
    onFileExtError : function(file) {}
  };
  function log(str) {
    var d = new Date();
    console.log('[' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + '] ' + str);
  };
  function humanizeSize(size) {
    var i = Math.floor(Math.log(size) / Math.log(1024));
    return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + [ 'B', 'kB', 'MB', 'GB', 'TB' ][i];
  };
  function addFile(uploader, id, file) {
    var $e = $(uploader.element);
    var t = uploader.settings.templatePreview;
    var c = $e.find('.' + uploader.settings.classFiles);
    t = $(t.replace('@id@', id).replace('@filesize@', humanizeSize(file.size)).replace('@filename@', file.name));
    t.attr('id', 'upload-container-' + id);
    if (!id) {
      c.empty();
    }
    c.prepend(t);
    if (typeof FileReader !== 'undefined' && file.type.match(/^image\//)) {
      var reader = new FileReader();
      var img = t.find('.image-preview').eq(0);
      reader.onload = function(e) {
        img.attr('src', e.target.result);
      }
      reader.readAsDataURL(file);
    } else {
      // Hide/Remove all Images if FileReader isn't supported
    }
  };
  function updateFileStatus(uploader, id, message) {
    var $e = $(uploader.element).find('#upload-container-' + id);
    var c = $e.find('.file-status').html(message);
  };
  function updateFileProgress(uploader, id, percent) {
    var $e = $(uploader.element).find('#upload-container-' + id);
    $e.find('.progress-bar').width(percent);
    $e.find('.file-status').html(percent + ' Complete');
    $e.find('.sr-only').html(percent + ' Complete');
  };
  var DmUploader = function(element, options) {
    var _this = this;
    this.element = $(element);
    this.settings = $.extend(defaults, {
      onInit : function() {
        log('Plugin initialized correctly');
      },
      onFallbackMode : function(message) {
        log(message);
      },
      onNewFile : function(id, file) {
        log('Adding new file');
        addFile(_this, id, file);
      },
      onBeforeUpload : function(id) {
        log('Starting the upload of #' + id);
        updateFileStatus(_this, id, 'Uploading...');
      },
      onComplete : function() {
        log('All pending tranfers completed');
      },
      onUploadProgress : function(id, percent) {
        log('#' + id + ' ' + percent + '% Complete...');
        updateFileProgress(_this, id, percent + '%');
      },
      onUploadSuccess : function(id, data) {
        log('Upload of file #' + id + ' completed');
        log('Server Response for file #' + id + ': ' + JSON.stringify(data));
        updateFileStatus(_this, id, '100%');
        updateFileProgress(_this, id, '100%');
      },
      onUploadError : function(id, message) {
        log('onUploadError#' + id + ' ' + message);
        updateFileStatus(_this, id, 'error:' + message);
      },
      onFileTypeError : function(file) {
        var msg = '@filename@(@filesize@) type not allowed.'.replace('@filename@', file.name).replace('@filesize@', humanizeSize(file.size));
        log(msg);
      },
      onFileSizeError : function(file) {
        var msg = '@filename@(@filesize@) size is too big.'.replace('@filename@', file.name).replace('@filesize@', humanizeSize(file.size));
        log(msg);
      },
      onFileExtError : function(file) {
        var msg = '@filename@(@filesize@) ext not allowed.'.replace('@filename@', file.name).replace('@filesize@', humanizeSize(file.size));
        log(msg);
      }
    }, options);
    if (!this.checkBrowser()) {
      return false;
    }
    this.init();
    this.element.on({
      'click' : function($evt) {
        $(this).closest('.file-preview').remove();;
      }
    }, '.icon-remove');
    return true;
  };
  DmUploader.prototype.checkBrowser = function() {
    if (window.FormData === undefined) {
      this.settings.onFallbackMode.call(this.element, 'Browser doesn\'t support From API');
      return false;
    }
    if (this.element.find('input[type=file]').length > 0) {
      return true;
    }
    if (!this.checkEvent('drop', this.element) || !this.checkEvent('dragstart', this.element)) {
      this.settings.onFallbackMode.call(this.element, 'Browser doesn\'t support Ajax Drag and Drop');
      return false;
    }
    return true;
  };
  DmUploader.prototype.checkEvent = function(eventName, element) {
    var element = element || document.createElement('div');
    var eventName = 'on' + eventName;
    var isSupported = eventName in element;
    if (!isSupported) {
      if (!element.setAttribute) {
        element = document.createElement('div');
      }
      if (element.setAttribute && element.removeAttribute) {
        element.setAttribute(eventName, '');
        isSupported = typeof element[eventName] == 'function';
        if (typeof element[eventName] != 'undefined') {
          element[eventName] = undefined;
        }
        element.removeAttribute(eventName);
      }
    }
    element = null;
    return isSupported;
  };
  DmUploader.prototype.init = function() {
    var widget = this;
    widget.queue = new Array();
    widget.queuePos = -1;
    widget.queueRunning = false;
    // -- Drag and drop event
    widget.element.on('drop', function(evt) {
      evt.preventDefault();
      var files = evt.originalEvent.dataTransfer.files;
      widget.queueFiles(files);
    });
    // -- Optional File input to make a clickable area
    widget.element.find('input[type=file]').on('change', function(evt) {
      var files = evt.target.files;
      widget.queueFiles(files);
      $(this).val('');
    });
    this.settings.onInit.call(this.element);
  };
  DmUploader.prototype.queueFiles = function(files) {
    var j = this.queue.length;
    for ( var i = 0; i < files.length; i++) {
      var file = files[i];
      // Check file size
      if ((this.settings.maxFileSize > 0) && (file.size > this.settings.maxFileSize)) {
        this.settings.onFileSizeError.call(this.element, file);
        continue;
      }
      // Check file type
      if ((this.settings.allowedTypes != '*') && !file.type.match(this.settings.allowedTypes)) {
        this.settings.onFileTypeError.call(this.element, file);
        continue;
      }
      // Check file extension
      if (this.settings.extFilter != null) {
        var extList = this.settings.extFilter.toLowerCase().split(';');
        var ext = file.name.toLowerCase().split('.').pop();
        if ($.inArray(ext, extList) < 0) {
          this.settings.onFileExtError.call(this.element, file);
          continue;
        }
      }
      this.queue.push(file);
      var index = this.queue.length - 1;
      this.settings.onNewFile.call(this.element, index, file);
    }
    // Only start Queue if we haven't!
    if (this.queueRunning) {
      return false;
    }
    // and only if new Failes were succefully added
    if (this.queue.length == j) {
      return false;
    }
    this.processQueue();
    return true;
  };
  DmUploader.prototype.processQueue = function() {
    var widget = this;
    widget.queuePos++;
    if (widget.queuePos >= widget.queue.length) {
      // Cleanup
      widget.settings.onComplete.call(widget.element);
      // Wait until new files are droped
      widget.queuePos = (widget.queue.length - 1);
      widget.queueRunning = false;
      return;
    }
    var file = widget.queue[widget.queuePos];
    // Form Data
    var fd = new FormData();
    fd.append(widget.settings.fileName, file);
    // Append extra Form Data
    $.each(widget.settings.extraData, function(exKey, exVal) {
      fd.append(exKey, exVal);
    });
    widget.settings.onBeforeUpload.call(widget.element, widget.queuePos);
    widget.queueRunning = true;
    // Ajax Submit
    $.ajax({
      url : widget.settings.url,
      type : widget.settings.method,
      dataType : widget.settings.dataType,
      data : fd,
      cache : false,
      contentType : false,
      processData : false,
      forceSync : false,
      xhr : function() {
        var xhrobj = $.ajaxSettings.xhr();
        if (xhrobj.upload) {
          xhrobj.upload.addEventListener('progress', function(event) {
            var percent = 0;
            var position = event.loaded || event.position;
            var total = event.total || e.totalSize;
            if (event.lengthComputable) {
              percent = Math.ceil(position / total * 100);
            }
            widget.settings.onUploadProgress.call(widget.element, widget.queuePos, percent);
          }, false);
        }
        return xhrobj;
      },
      success : function(data, message, xhr) {
        widget.settings.onUploadSuccess.call(widget.element, widget.queuePos, data);
      },
      error : function(xhr, status, errMsg) {
        widget.settings.onUploadError.call(widget.element, widget.queuePos, errMsg);
      },
      complete : function(xhr, textStatus) {
        widget.processQueue();
        widget.element.find('.'+widget.settings.classDebug).html(xhr.responseText);
      }
    });
  }
  $.fn.dmUploader = function(options) {
    return this.each(function() {
      if (!$.data(this, pluginName)) {
        $.data(this, pluginName, new DmUploader(this, options));
      }
    });
  };
  // -- Disable Document D&D events to prevent opening the file on browser when
  // we drop them
  $(document).on('dragenter', function(e) {
    e.stopPropagation();
    e.preventDefault();
  });
  $(document).on('dragover', function(e) {
    e.stopPropagation();
    e.preventDefault();
  });
  $(document).on('drop', function(e) {
    e.stopPropagation();
    e.preventDefault();
  });
})(jQuery);
